#include "utils/command_line_parser.h"

#include <algorithm>
#include <sstream>
#include <cstring>

CommandLineParser::CommandLineParser() { registerOptions(); }

void CommandLineParser::registerOptions() {
    /// Help and version
    registerOptions("help", 'h', "Display this help message and exit");
    registerOptions("version", 'v', "Display version information and exit");

    /// Device configuration
    registerOptions("device", 'd', "Network interface to monitor", true, "INTERFACE");
    registerOptions("list-devices", 'l', "List all available network interface");
    registerOptions("promiscuous", 0, "Enable promiscuous mode (default)");
    registerOptions("no-promiscuous", 0, "Disable promiscuous mode");

    /// Filtering rules
    registerOptions("filter", 'f', "Set BPF filter expression *", true, "EXPRESSION");
    registerOptions("rules", 'r', "Load filtering rules from file *", true, "FILE");
    registerOptions("allow-all", 0, "Allow all traffic (test mode)");
    registerOptions("deny-all", 0, "Deny all traffic (test mode)");

    /// Performance tuning
    registerOptions("buffer-size", 'b', "Set capture buffer size", true, "SIZE");
    registerOptions("timeout", 't', "Set capture timeout", true, "MILLISECONDS");
    registerOptions("workers", 0, "Set number of worker threads *", true, "NUM");
    registerOptions("batch-size", 0, "Set batch processing size", true, "NUM");

    /// Logging and output
    registerOptions("log-level", 'L', "Set log level (debug/info/warning/error)", true, "LEVEL");
    registerOptions("log-file", 'F', "Set log file path *", true, "FILE");
    registerOptions("console-only", 0, "Output logs to console only");
    registerOptions("quiet", 'q', "Quiet mode, only output errors");
    registerOptions("verbose", 0, "Verbose output mode");

    /// Operation modes
    registerOptions("test", 'T', "Test mode, do not forward packets *");
    registerOptions("dry-run", 0, "Dry run mode, parse configuration only");
    registerOptions("daemon", 0, "Run as daemon *");
}

void CommandLineParser::registerOptions(const std::string& longName, char shortName,
                                       const std::string& description,
                                       bool requiresArgument,
                                       const std::string& argumentName) {
    Option option(longName, shortName, description, requiresArgument, argumentName);
    m_options.push_back(option);
    m_longOptionMap[longName] = option;
    if (shortName != 0) {
        m_shortOptionMap[shortName] = option;
    }
}

bool CommandLineParser::parse(int argc, char* argv[]) {
    for (int i = 1; i < argc; ++i) {
        std::string arg = argv[i];

        if (arg == "--") {
            break;
        } else if (startWith(arg, "--")) {
            std::string optionName = arg.substr(2);
            std::string value;

            size_t equalsPos = optionName.find('=');
            if (equalsPos != std::string::npos) {
                value = optionName.substr(equalsPos + 1);
                optionName = optionName.substr(0, equalsPos);
            } else {
                if (i + 1 < argc && !startWith(argv[i + 1], "-")) {
                    value = argv[++i];
                }
            }

            if (!handleOption(optionName, value)) {
                std::cerr << "Error: Unknown option --" << optionName
                          << " " << value << std::endl;
                std::cerr << "Use -h or --help for usage";
                return false;
            }
        } else if (startWith(arg, "-") && arg.length() > 1) {
            std::string options = arg.substr(1);

            char optionChar = options[0];
            std::string value;

            auto it = m_shortOptionMap.find(optionChar);
            if (it != m_shortOptionMap.end() && it->second.requiresArgument) {
                if (1 == options.length()) {
                    if (i + 1 < argc) {
                        value = argv[++i];
                    } else {
                        std::cerr << "Error: Option -" << optionChar
                                  << " requires an argument" << std::endl;
                        return false;
                    }
                } else {
                    value = options.substr(1);
                    break;
                }
            }

            if (!handleOption(std::string(1, optionChar), value)) {
                std::cerr << "Error: Unknown option -" << optionChar << std::endl;
                return false;
            }
        } else {
            /// Default treat as device name.
            m_devices.emplace_back(arg);
        }
    }

    validateOptions();
    return true;
}

bool CommandLineParser::handleOption(const std::string& option, const std::string& value) {
    std::string optionName(option);
    /// Handle by short name.
    if (option.length() == 1) {
        char shortName = option[0];
        auto it = m_shortOptionMap.find(shortName);
        if (it == m_shortOptionMap.end()) {
            return false;
        }
        optionName = it->second.longName;
    }

    /// Handle by long name.
    auto it = m_longOptionMap.find(optionName);
    if (it == m_longOptionMap.end()) {
        return false;
    }

    const Option& opt = it->second;

    if (opt.requiresArgument && value.empty()) {
        std::cerr << "Error: Option --" << opt.longName << " requires an argument";
        return false;
    }

    /// Process the option
    if (opt.longName == "help") {
        m_showHelp = true;
    } else if (opt.longName == "version") {
        m_showVersion = true;
    } else if (opt.longName == "list-devices") {
        m_listDevices = true;
    } else if (opt.longName == "device") {
        m_devices.emplace_back(value);
    } else if (opt.longName == "promiscuous") {
        m_promiscuous = true;
    } else if (opt.longName == "no-promiscuous") {
        m_promiscuous = false;
    } else if (opt.longName == "filter") {
        m_filter = value;
    } else if (opt.longName == "rules") {
        m_rulesFile = value;
    } else if (opt.longName == "buffer-size") {
        try {
            m_bufferSize = std::stoi(value);
        } catch (const std::exception& e) {
            std::cerr << "Error: Invalid buffer size: " << value << std::endl;
            return false;
        }
    } else if (opt.longName == "timeout") {
        try {
            m_timeout = std::stoi(value);
        } catch (const std::exception& e) {
            std::cerr << "Error: Invalid timeout: " << value << std::endl;
            return false;
        }
    } else if (opt.longName == "workers") {
        try {
            m_workers = std::stoi(value);
        } catch (const std::exception& e) {
            std::cerr << "Error: Invalid worker count: " << value << std::endl;
        }
    } else if (opt.longName == "batch-size") {
        try {
            m_batchSize = std::stoi(value);
        } catch (const std::exception& e) {
            std::cerr << "Error: Invalid batch size: " << value << std::endl;
        }
    } else if (opt.longName == "log-level") {
        m_logLevel = value;
    } else if (opt.longName == "console-only") {
        m_consoleOnly = true;
    } else if (opt.longName == "quiet") {
        m_quiet = true;
        m_logLevel = "error";
    } else if (opt.longName == "verbose") {
        m_verbose = true;
        m_logLevel = "debug";
    } else if (opt.longName == "test") {
        m_testMode = true;
    } else if (opt.longName == "daemon") {
        m_daemon = true;
    }

    return true;
}

void CommandLineParser::validateOptions() {
    if (m_quiet && m_verbose) {
        std::cerr << "Warning: Both --quiet and --verbose specified, using --verbose" << std::endl;
        m_quiet = false;
    }
}

void CommandLineParser::printHelp() const {
    std::cout << "Usage: hp_filter [OPTIONS]... \n";
    std::cout << "A high-performance packet filter gateway.\n\n";

    std::cout << "Options:\n";
    for (const auto& opt : m_options) {
        std::cout << "    ";
        if (opt.shortName != 0) {
            std::cout << "-" << opt.shortName << ", ";
        } else {
            std::cout << "    ";
        }
        std::cout << "--" << opt.longName;
        if (opt.requiresArgument) {
            std::cout << " " << opt.argumentName;
        }
        std::cout << "\n          " << opt.description << "\n\n";
    }
    std::cout << "Examples:\n";
    std::cout << "  hp_filter -d eth0 -f \"tcp port 80\" --log-level debug\n";
    std::cout << "  hp_filter --device eth0 --device=eth1 --rules /etc/gateway/rules.conf\n";
    std::cout << "  hp_filter -L\n";
    std::cout << "\n";
    std::cout << "Notice, the arguments which the description end of '*' are not able to use currently.\n";
}

void CommandLineParser::printVersion() const {
    std::cout << "hp_filter version 1.0.0\n";
    std::cout << "A high-performance packet filter gateway\n";
    std::cout << "Built with C++20 and libpcap\n";
}

bool CommandLineParser::startWith(const std::string& str, const std::string& prefix) {
    return str.compare(0, prefix.length(), prefix) == 0;
}